package cn.com.idmy.id.segment;

import cn.com.idmy.id.lifecycle.AbstractIdGeneratorLifecycle;
import cn.com.idmy.id.segment.bus.FillSegmentUpdater;
import cn.com.idmy.id.segment.model.IdState;
import cn.com.idmy.id.segment.policy.FetchPolicy;
import cn.com.idmy.id.service.IdGeneratorService;
import cn.com.idmy.id.service.LeafService;
import lombok.extern.slf4j.Slf4j;
import org.dromara.hutool.core.collection.set.ConcurrentHashSet;
import org.jetbrains.annotations.NotNull;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
public class CacheSegmentIdGeneratorService extends AbstractIdGeneratorLifecycle implements IdGeneratorService {
    private final EnhanceThreadLocal threadLocal = new EnhanceThreadLocal();
    private final ConcurrentHashMap<String, SegmentBuffer> segmentBufferMap = new ConcurrentHashMap<>(256);
    private final FetchPolicy fetchPolicy;
    private final LeafService leafService;
    private final FillSegmentUpdater fillSegmentUpdater;
    private final ConcurrentHashSet<Thread> threadSet = new ConcurrentHashSet<>(256);

    public CacheSegmentIdGeneratorService(LeafService leafService, FetchPolicy fetchPolicy) {
        this.leafService = leafService;
        this.fillSegmentUpdater = new FillSegmentUpdater();
        this.fetchPolicy = fetchPolicy;
    }

    @Override
    public long generateId(@NotNull String key) {
        return fetchPolicy.threadLocalCacheEnabled() ? getIdForThreadLocal(key) : getIdForSegment(key);
    }

    /**
     * 从ThreadLocal中获取id
     * <p>
     * TODO (不建议,待优化)
     * 存在线程挂掉之后，无法及时清空segmentBufferMap，
     *
     * @param key
     * @return
     */
    private long getIdForThreadLocal(String key) {
        //查看 tl 中是否有缓存
        log.info("获取ID,key:{}", key);
        Map<String, IdState> map = threadLocal.get();
        IdState IdState = map.get(key);
        if (isFill(IdState)) {
            log.info("tl中无缓存，开始填充缓存,key:{}", key);
            SegmentBuffer segmentBuffer = segmentBufferMap.get(key);
            if (segmentBuffer == null) {
                //若二级缓存没有,则开始初始化
                synchronized (this) {
                    if ((segmentBuffer = segmentBufferMap.get(key)) == null) {
                        log.info("二级缓存中无缓存，开始填充缓存,key:{}", key);
                        //这里的maxid curid 是初始值0
                        segmentBuffer = new SegmentBuffer(leafService, fillSegmentUpdater, fetchPolicy, key);
                        segmentBufferMap.put(key, segmentBuffer);
                    }
                }
            }
            boolean hasException = false;
            try {
                IdState = segmentBuffer.nextID(fetchPolicy.threadLocalFetchSize(key));
            } catch (Exception e) {
                hasException = true;
                //捕捉到异常 取消缓存
                log.info("获取id异常：{}", e.getMessage());
                //幂等删除
                segmentBufferMap.remove(key);
                throw e;
            } finally {
                threadSet.add(Thread.currentThread());
                if (!hasException) {
                    map.put(key, IdState);
                }
            }
        }
        long curId = IdState.getCurId();
        IdState.setCurId(curId + 1);
        return curId;
    }

    private long getIdForSegment(String key) {
        SegmentBuffer segmentBuffer = segmentBufferMap.get(key);
        if (segmentBuffer == null) {
            //若二级缓存没有,则开始初始化
            synchronized (this) {
                if ((segmentBuffer = segmentBufferMap.get(key)) == null) {
                    log.info("二级缓存中无缓存，开始填充缓存, key:{}", key);
                    // 这里的 maxId curId 是初始值0
                    segmentBuffer = new SegmentBuffer(leafService, fillSegmentUpdater, fetchPolicy, key);
                    segmentBufferMap.put(key, segmentBuffer);
                }
            }
        }
        try {
            IdState IdState = segmentBuffer.nextID(1);
            return IdState.getCurId();
        } catch (Exception e) {
            //捕捉到异常 取消缓存
            log.info("获取 id 异常,取消 segment 缓存,等待下一次填充：{}", e.getMessage());
            //幂等删除
            segmentBufferMap.remove(key);
            throw e;
        }
    }


    private boolean isFill(IdState IdState) {
        //左开右闭
        return IdState == null || IdState.getCurId() > IdState.getMaxId() - 1;
    }

    /**
     * 删除相关键的缓存
     *
     * @return
     * @deprecated 这个方法有隐患
     */
    @Deprecated
    public boolean deleteSegment(String key) {
        //循环删除
        threadSet.forEach(thread -> {
            Map<String, IdState> value = threadLocal.get(thread);
            if (value != null) {
                if (value.remove(key) != null) {
                    log.info("thread: {}, removed key: {}", thread, key);
                }
            }
            segmentBufferMap.remove(key);
        });
        return false;

    }

    @Override
    public String name() {
        return "segment generator";
    }


    private static class EnhanceThreadLocal {
        /**
         * 2级缓存
         * key:threadId  value:(key:bzId,value:idInfo)
         */
        private final Map<Thread, Map<String, IdState>> map = new ConcurrentHashMap<>(256);

        public Map<String, IdState> get() {
            Thread thread = Thread.currentThread();
            return map.computeIfAbsent(thread, k -> new HashMap<>(256));
        }

        public Map<String, IdState> get(Thread thread) {
            return map.get(thread);
        }
    }
}
